package com.example.mq.mqserver.core;

import com.example.mq.common.exception.MqException;

/**
 * 该类用于实现交换机的转发规则，并验证 bindingKey 的合法性。
 */
public class Router {

    /**
     * 检查两个相邻部分是否为非法的通配符组合。
     *
     * @param first  第一个部分
     * @param second 第二个部分
     * @return 如果是非法组合返回 true，否则返回 false
     */
    private boolean isInvalidWildcardCombination(String first, String second) {
        return (first.equals("#") && (second.equals("#") || second.equals("*"))) ||
                (first.equals("*") && second.equals("#"));
    }

    /**
     * 检查 bindingKey 的合法性。
     * bindingKey 构造规则:
     * 1. 只能包含字母、数字、下划线。
     * 2. 使用 . 分割成若干部分。
     * 3. 允许 * 和 # 作为通配符，但通配符只能作为独立的部分存在。
     *
     * @param bindingKey 绑定键
     * @return 如果 bindingKey 合法返回 true，否则返回 false
     */
    public boolean checkBindingKey(String bindingKey) {
        if (bindingKey.isEmpty()) {
            // 空字符串在某些交换机类型中是合法的
            return true;
        }
        // 使用正则表达式验证 bindingKey 的合法性
        if (!bindingKey.matches("^[\\w#*]+(\\.[\\w#*]+)*$")) {
            return false;
        }

        // 检查 * 和 # 是否作为独立部分存在
        String[] words = bindingKey.split("\\.");
        for (String word : words) {
            if ((word.contains("*") || word.contains("#")) && word.length() > 1) {
                return false;
            }
        }

        // 检查非法相邻的通配符组合
        for (int i = 0; i < words.length - 1; i++) {
            if (isInvalidWildcardCombination(words[i], words[i + 1])) {
                return false;
            }
        }

        return true;
    }

    /**
     * 检查 routingKey 的合法性。
     * routingKey 构造规则:
     * 1. 只能包含字母、数字、下划线。
     * 2. 使用 . 分割成若干部分。
     *
     * @param routingKey 路由键
     * @return 如果 routingKey 合法返回 true，否则返回 false
     */
    public boolean checkRoutingKey(String routingKey) {
        if (routingKey.isEmpty()) {
            // 空字符串在某些交换机类型中是合法的
            return true;
        }
        // 使用正则表达式验证 routingKey 的合法性
        return routingKey.matches("^[\\w]+(\\.[\\w]+)*$");
    }

    /**
     * 根据交换机类型判断消息是否可以转发到绑定的队列。
     *
     * @param exchangeType 交换机类型
     * @param binding      绑定信息
     * @param message      消息
     * @return 如果可以转发返回 true，否则返回 false
     * @throws MqException 当交换机类型非法时抛出异常
     */
    public boolean route(ExchangeType exchangeType, Binding binding, Message message) throws MqException {
        switch (exchangeType) {
            case FANOUT:
                return true; // FANOUT 类型交换机会转发到所有绑定的队列
            case TOPIC:
                return routeTopic(binding, message); // TOPIC 主题交换机的转发规则较复杂
            default:
                throw new MqException("[Router] 交换机类型非法! exchangeType=" + exchangeType);
        }
    }

    /**
     * 处理 TOPIC 类型交换机的路由规则。
     *
     * @param binding 绑定信息
     * @param message 消息
     * @return 如果匹配成功返回 true，否则返回 false
     */
    private boolean routeTopic(Binding binding, Message message) {


        // 将以"."分割的key分离出来
        String[] bindingTokens = binding.getBindingKey().split("\\.");
        String[] routingTokens = message.getRoutingKey().split("\\.");

        int bindingIndex = 0;
        int routingIndex = 0;

        while (bindingIndex < bindingTokens.length && routingIndex < routingTokens.length) {
            String bindingToken = bindingTokens[bindingIndex];
            String routingToken = routingTokens[routingIndex];

            if (bindingToken.equals("*")) {
                // * 可以匹配任意一个部分
                bindingIndex++;
                routingIndex++;
            } else if (bindingToken.equals("#")) {
                bindingIndex++;
                if (bindingIndex == bindingTokens.length) {
                    // # 后面没有其他部分，匹配成功
                    return true;
                }

                // 查找下一个匹配的部分
                routingIndex = findNextMatch(routingTokens, routingIndex, bindingTokens[bindingIndex]);
                if (routingIndex == -1) {
                    return false; // 未找到匹配项
                }
                bindingIndex++;
                routingIndex++;
            } else {
                if (!bindingToken.equals(routingToken)) {
                    return false; // 普通部分不匹配
                }
                bindingIndex++;
                routingIndex++;
            }
        }

        // 双方必须同时到达末尾，才算匹配成功
        return bindingIndex == bindingTokens.length && routingIndex == routingTokens.length;
    }

    /**
     * 查找 routingTokens 中与 bindingToken 匹配的下一个部分。
     *
     * @param routingTokens 路由键分段
     * @param startIndex    开始查找的索引
     * @param bindingToken  绑定键的当前部分
     * @return 匹配的索引位置，如果未找到返回 -1
     */
    private int findNextMatch(String[] routingTokens, int startIndex, String bindingToken) {
        for (int i = startIndex; i < routingTokens.length; i++) {
            if (routingTokens[i].equals(bindingToken)) {
                return i;
            }
        }
        return -1;
    }
}
